package team.dmh.wx.netty;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import team.dmh.wx.entity.ChatMsg;
import team.dmh.wx.enums.ChatMsgFlagEnum;
import team.dmh.wx.enums.MsgOpsTypeEnum;
import team.dmh.wx.service.IChatMsgService;
import team.dmh.wx.service.IFriendService;
import team.dmh.wx.util.IDUtils;
import team.dmh.wx.util.SpringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Auther: Hz1kai
 * @Date: 2018/10/14 21:18
 * @Description: 消息处理类，TextWebSocketFrame作消息载体，用于处理WebSocket文本
 */
@Slf4j
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    //管理channel
    public static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    //用户channel映射
    public static Map<String, Channel> users = new HashMap<>();
    //channelId和userId映射
    public static Map<String, String> idRelation = new HashMap<>();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {
        //获取文本
        String text = textWebSocketFrame.text();
        Content content = JSON.parseObject(text, new TypeReference<Content>() {
        });
        log.info("--------------接受消息------------:" + content);
        Integer type = content.getType();
        Channel channel = ctx.channel();
        ChatMsgInput chatMsgInput = content.getChatMsgInput();
        IChatMsgService chatMsgService = SpringUtils.getBean(IChatMsgService.class);
        IFriendService friendService = SpringUtils.getBean(IFriendService.class);
        //判断消息类型进行分别处理
        switch (MsgOpsTypeEnum.getByType(type)) {
            //进行channel与用户绑定
            case CONNECT:
                String senderId = chatMsgInput.getSenderId();
                users.put(senderId, channel);
                idRelation.put(channel.id().asShortText(), senderId);
                log.info("add user id : {} ,current users size : {}", senderId, users.size());
                break;
            //将聊天记录标记为未读并保存
            case CHAT:
                if (!friendService.isExist(chatMsgInput.getAccepterId(), chatMsgInput.getSenderId())) {
                    Content sendContent = new Content().setType(MsgOpsTypeEnum.FRIEND_ERROR.type)
                            .setChatMsgInput(new ChatMsgInput().setMsg(MsgOpsTypeEnum.FRIEND_ERROR.msg));
                        channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(sendContent)));
                    return;
                }
                //保存
                ChatMsg chatMsg = new ChatMsg()
                        .setSendUserId(chatMsgInput.getSenderId())
                        .setAcceptUserId(chatMsgInput.getAccepterId())
                        .setMsg(chatMsgInput.getMsg())
                        .setFlag(ChatMsgFlagEnum.UN_SIGN.type)
                        .setId(IDUtils.getInstance().nextId());
                chatMsgService.save(chatMsg);
                String senderAlias = chatMsgService.getSenderAlias(chatMsgInput.getSenderId(), chatMsgInput.getAccepterId());
                Content sendContent = new Content().setType(MsgOpsTypeEnum.CHAT.type)
                        .setChatMsgInput(chatMsgInput.setMsgId(chatMsg.getId())
                                .setSenderAlias(senderAlias));


                //消息转发
                String accepterId = chatMsgInput.getAccepterId();
                Channel accepterChannel = users.get(accepterId);
                if (accepterChannel != null) {
                    if (clients.find(accepterChannel.id()) != null) {
                        //接受消息用户在线
                        log.info("--------------转发消息------------:" + sendContent);
                        accepterChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(sendContent)));
                    } else {
                        //todo 接受用户离线消息推送
                    }
                } else {
                    //todo 消息推送
                }
                break;
            //将聊天记录标记已读
            case SIGNED:
                String msgIdStr = (String) content.getExtend();
                String msgIds[] = msgIdStr.split(",");
                List<ChatMsg> chatMsgList = new ArrayList<>();
                for (String id : msgIds) {
                    chatMsgList.add(new ChatMsg().setId(id).setFlag(ChatMsgFlagEnum.SIGN.type));
                }
                chatMsgService.updateBatchById(chatMsgList);
                break;
            //心跳检测保持连接
            case KEEP_ALIVE:
                log.info("accept heartbeat from channel : " + channel.id().asShortText());
                break;
            case PULL_FRIEND:
                break;
            case PULL_FRIEND_REQUEST:
                break;
        }

        //两种方式都可以
        /*for(Channel channel:clients){
            channel.writeAndFlush(new TextWebSocketFrame("send msg from server : "+text));
        }
        clients.writeAndFlush(new TextWebSocketFrame("send msg from server : "+text));*/
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("channel connected id : " + ctx.channel().id().asLongText());
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //可以省略，当触发移除事件时会自动在ChannelGroup中删除
//        clients.remove(ctx.channel());
        //short id从long id中截取，为一部分
        log.info("client disconnect id : {} ", ctx.channel().id().asLongText());
        Channel channel = ctx.channel();
        String userId = idRelation.get(channel.id().asShortText());
        users.remove(userId);
        idRelation.remove(channel.id().asShortText());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        Channel channel = ctx.channel();
        String userId = idRelation.get(channel.id().asShortText());
        users.remove(userId);
        idRelation.remove(channel.id().asShortText());
        clients.remove(channel);
        channel.close();
    }
}
